package igin

import (
	"context"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/rs/zerolog"
	"net/http"
	"time"
)

type OptionHandle func(h *Http)

type Http struct {
	conf   Conf
	Engine *gin.Engine
	Server *http.Server
	Log    zerolog.Logger
	ctx    context.Context
	cancel context.CancelCauseFunc
	quit   chan struct{}
}

func NewHttp(ctx context.Context, conf Conf, log zerolog.Logger, options ...OptionHandle) *Http {

	ctx, cancel := context.WithCancelCause(ctx)

	gin.SetMode(conf.Mode)

	h := &Http{
		conf:   conf,
		Engine: gin.New(),
		Log:    log,
		ctx:    ctx,
		cancel: cancel,
		quit:   make(chan struct{}),
	}

	//更新配置
	if len(options) > 0 {
		h.WithOptions(options...)
	}

	return h
}

func (h *Http) WithOptions(options ...OptionHandle) *Http {
	for _, option := range options {
		option(h)
	}
	return h
}

func (h *Http) start() {
	if h.conf.QuitTimeout <= 0 {
		h.conf.QuitTimeout = DefaultQuitTimeout
	}

	if h.conf.ReadHeaderTimeout <= 0 {
		h.conf.ReadHeaderTimeout = DefaultReadHeaderTimeout
	}

	if h.conf.ReadTimeout <= 0 {
		h.conf.ReadTimeout = DefaultReadTimeout
	}

	if h.conf.WriteTimeout <= 0 {
		h.conf.WriteTimeout = DefaultWriteTimeout
	}

	if h.conf.IdleTimeout <= 0 {
		h.conf.IdleTimeout = DefaultIdleTimeout
	}

	//设置服务
	h.Server = &http.Server{
		Addr:                         h.conf.Addr,
		Handler:                      h.Engine,
		DisableGeneralOptionsHandler: h.conf.DisableGeneralOptionsHandler,
		ReadTimeout:                  time.Duration(h.conf.ReadTimeout) * time.Second,
		ReadHeaderTimeout:            time.Duration(h.conf.ReadHeaderTimeout) * time.Second,
		WriteTimeout:                 time.Duration(h.conf.WriteTimeout) * time.Second,
		IdleTimeout:                  time.Duration(h.conf.IdleTimeout) * time.Second,
		MaxHeaderBytes:               h.conf.MaxHeaderBytes,
	}

	h.Log.Info().Msgf("listen %s", h.Server.Addr)

	var err error
	//启用tls/ssl
	if h.conf.Tls {
		err = h.Server.ListenAndServeTLS(h.conf.TlsCert, h.conf.TlsKey)
	} else {
		err = h.Server.ListenAndServe()
	}

	if err != nil {
		if !errors.Is(err, http.ErrServerClosed) {
			h.Log.Fatal().Msgf("listen: %s err:%s", h.Server.Addr, err)
			h.cancel(fmt.Errorf("listen: %s err:%s", h.Server.Addr, err))
		}
	}
}

func (h *Http) Conf() Conf {
	return h.conf
}

func (h *Http) Run() {
	defer h.cancel(nil)

	go h.start()

	<-h.ctx.Done()

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(h.conf.QuitTimeout)*time.Second)
	defer cancel()

	if err := h.Server.Shutdown(ctx); err != nil {
		h.Log.Fatal().Msgf("shutdown err:%s", err.Error())
	} else {
		h.Log.Info().Msg("shutdown success")
	}
	h.quit <- struct{}{}
}

func (h *Http) Close(e error) *Http {
	h.cancel(e)
	return h
}

func (h *Http) Complete() {
	<-h.quit
}
